RiverSync
SPEC-ERD · v0.26
28 June 2026
Owner: Platform team

Platform data model — master

The structural reading of the PRD set. This master holds the domain map, the integrity conditions and the requirement traceability; one drill-down document per application details the entities that app touches. Everything renders from one central catalog, so no definition can drift between documents.

DraftDerived — SPEC-PRD + app PRDs + prototypes
Centralized system. Entities and DM-rules are defined once in erd/erd-entities.js; diagram layouts once in erd/erd-diagrams.js. Every ERD document — this master and the five drill-downs — composes from that catalog. Requirements live in SPEC-PRD and the app PRDs; on any conflict the master PRD wins.

1The ERD document set

DocumentIdDrills into
Master — this documentSPEC-ERDDomain map · DM conditions · traceability · open questions
AccountSPEC-ERD-ACCIdentity & authorization · org structure · partner surface · billing & audit
PortalSPEC-ERD-PTLDevice monitoring · alerts · service & messaging
PartnersSPEC-ERD-PARAgreements & scope · workload · renewals
PipelineSPEC-ERD-PIPDeals · quotes · provisioning handoff
AdminSPEC-ERD-ADMTenants & plans · provisioning · audit & view-as
FieldSPEC-ERD-FLDOn-site visit · runbook tasks · parts · evidence · sign-off · maintenance plans

2How the PRDs correlate

Each PRD contributes a different slice of one model. The correlating spine is Organization → Device → MaintenanceAgreement: the same MaintenanceAgreement record is managed in Account, displayed in Portal, worked in Partners, created from a won deal in Pipeline, and attached at provisioning in Admin — every record pointing at one shared MaintenanceTier (Silver · Gold).

EntityAccountPortalPartnersPipelineAdminField
Tenant & organizationR·URC·R·U·D
User accounts & rolesC·R·U·DRRRR
Departments, regions & sitesC·R·U·DRRR⁷
DeviceRR·U²RCR
Products & telemetry schemaR⁸RC·R·U
Maintenance agreementR·U³RRCCR
Partner link & access grantR·U·DRRU⁴R
Distribution agreement & channelRC·R·UR
Partner program tier & reviewR⁹RC·R·UR
Alerts & telemetryR·U²RR
Tickets & visitsC·R·UR·U¹RC·R·U⁷
Field execution & maintenance plansRRC·R·U⁷
Deal & quoteC·R⁵C·R·U·DR
Billing & invoicesR·UC·R·U
Audit eventsRC⁶C⁶C⁶C·RC⁶

C create · R read · U update · D delete · ¹ within the customer-granted scope only (PRT-3, PAR-1) · ² acknowledge / control where the Portal role allows (PTL-2, PTL-5) · ³ move-at-renewal action (PRT-2) · ⁴ a won partner deal creates or extends the link (PIP-3) · ⁵ resellers register deals and partners read their funnel from the Partners app, value/quote masked off-channel (PAR-6, PRT-12) · ⁶ every app writes audit events; reading is Account (customer log) and Admin (RiverSync trail) · ⁷ Field writes the on-site execution records (tasks, parts, evidence, sign-off) and reads site/device context within the engineer's visit assignment only (DM-27, DM-31). ⁸ Portal reads the product model + resolved telemetry schema to render each device's data; the catalog & schema registry are RiverSync-managed (PRO-1…6). ⁹ Account reads a reseller's ProgramTier only for the partner-list chip; the tier and its PartnerTierReview history are RiverSync-owned in Sales, set in Pipeline (PRT-14, PRT-16).

RiverSync Co., Ltd. · BangkokSPEC-ERD · 1 of 5

3Domain map & notation

The model splits into five domains; the drill-down documents detail each one. Naming: PascalCase for entities and attributes — every word capitalized (CustomerId, PeriodEnd), matching the .NET codebase; the Postgres DDL maps them to snake_case identifiers (device_agreement.period_end) so nothing needs quoting. Crow's-foot notation:

exactly one zero or one one or many zero or many PK primary · FK foreign · PF both · UQ unique
Tags name the drill-down documents that detail each domain. Dashed lines are derived or lifecycle relations.D-0

Products & telemetry taxonomy. The device model is an entity, not an enum (v0.21): ProductFamily → ProductModel is the commercial catalog (frigo · koelkast · nevera and their models), and each model declares a telemetry schema. TelemetrySchemaFamily is the flat registry of telemetry shapes — refrigeration is family #1 (the existing 60-column schema); the rest were discovered from the live fleet. DeviceSchemaAssignment resolves each device's IoT-Hub id to its actual (family, version). The detailed drill-down is the Product Catalog (SPEC-ERD-PRO).

Sources: PRO-1…6 · DM-35–38 · Telemetry/Discovery family-inventory.json. Device.ModelKey and QuoteLine.ModelKey replace the former Model enum.D-T

4Integrity conditions

The DM-rules extracted from the requirements and the prototypes — defined once in the central catalog, repeated in each drill-down where they apply. Sources cited per rule.

RiverSync Co., Ltd. · BangkokSPEC-ERD · 2 of 5

5Requirement traceability

Every requirement in the PRD set, mapped to where it lands in the model — the cross-check that keeps PRD and ERD aligned. No orphans in either direction: every requirement ID has a row here, and every DM-rule cites a requirement or prototype. Rows marked are acknowledged gaps.

ReqWhere it lands in the model
Platform · master (SPEC-PRD) — identity foundation: ASP.NET Core Identity 10 (Guid keys), extended not replaced (DM-32, DM-33)
TEN-1Tenant.Type enum: riversync · customer · partner — D-1
TEN-2Tenant.Roles — customer + partner on one org (DM-3)
TEN-3Organization 0..1 per tenant, NULL ⇔ riversync (DM-2)
ID-1ApplicationUser.TenantId — exactly one tenant (DM-1, DM-32)
ID-2UNIQUE (Email, TenantId) overrides Identity's NormalizedEmail index + account chooser (DM-1)
ID-3UserLogin «AspNetUserLogins» (Google · MS · LinkedIn); email & password = AspNetUsers.PasswordHash; Organization.Sso = Entra ID
ID-4 ⚠Federated session — runtime state (AspNetUserTokens hold refresh/2FA, not the session itself)
AUTH-1ApplicationUserRole «AspNetUserRoles» — UNIQUE (UserId, AppKey), AppKey extension (DM-4)
AUTH-2Application.Gating: open · partner · riversync; gated apps hidden (DM-4)
AUTH-3Access surfaces read ApplicationUserRole · ApplicationRole · Permission (Users, User Detail, Roles, Permissions)
AUTH-4Cascades from DM-2 — no Organization row, no org-scoped tables for riversync
AUTH-5ApplicationRole rows in the riversync tenant's set: admin · support · sales · accounting · engineer (DM-5)
AUTH-6Removed in PRD v0.13 — one role per application via ApplicationUserRole; no multi-role table (DM-4)
AUTH-7RolePermission matrix → compiled to AspNetRoleClaims/AspNetUserClaims; admin fixed via ApplicationRole.IsFixed (DM-5, DM-6, DM-33)
PRT-1MaintenanceAgreement per device; PartnerCustomer derived from active agreements (DM-12, DM-14)
PRT-2MaintenanceAgreement.PeriodEnd per device; SuccessorOrganization schedules the move at renewal (DM-14)
PRT-3AccessGrant — four scope switches + RegionLimit, effective immediately (DM-16)
PRT-4AuditEvent.AttributedOrganization — partner attribution in the customer's log (DM-20)
PRT-5MaintenanceTier catalog (Silver · Gold), referenced by MaintenanceAgreement.MaintenanceTierKey, vs PartnerProfile.ProgramTier (Authorized · Gold · Platinum) (DM-17, DM-34)
PRT-6RiverSync is never set as a device's PartnerId; implicit factory access, audited (DM-15)
PRT-7Partner detail tabs read Device · MaintenanceAgreement · AccessGrant · AuditEvent (agreement)
PRT-8CHECK CustomerId ≠ PartnerId on PartnerCustomer (DM-13)
PRT-9PartnerProfile.Subtype enum: distributor · reseller — exactly one, platform-wide, RiverSync-set (DM-22)
PRT-10DistributionAgreement — reseller ↔ distributor, several active per reseller, subtype CHECKs (DM-23)
PRT-11Deal.DistributorOrganization XOR DirectException; RiverSync reallocation audited (DM-24)
PRT-12Distributor funnel reads masked off-channel — value & quote detail on own-channel deals only (DM-25)
PRT-13Partner-role confidentiality — customer-facing partner surfaces read only PartnerCustomer + AccessGrant, never the partner organization's other tenant roles (DM-26)
PRT-14PartnerProfile.ProgramTier (authorized·gold·platinum) — reseller subtype only, NULL for distributors; RiverSync-set (DM-39)
PRT-15Tier→entitlement resolution — deal cap, pricing discount, protection window, leads, direct-exception eligibility, MDF, early access (DM-40)
PRT-16PartnerTierReview — scorecard (CertifiedEngineers · TrailingRevenue · Csat) + grant·hold·downgrade; tier changes only via a review (DM-41)
PRT-17Reseller→distributor conversion — Subtype flip clears ProgramTier, reconciles deals, registers DistributionAgreements (DM-42)
Products & telemetry taxonomy (SPEC-PRD) — the device model becomes an entity; the telemetry-schema registry it resolves to (DM-35–38, Initiative 015)
PRO-1ProductFamily — frigo · koelkast · nevera; the commercial product line (DM-35)
PRO-2ProductModel — the model within a family; Device.ModelKey + QuoteLine.ModelKey FKs replace the Model enum (DM-35)
PRO-3TelemetrySchemaFamily — the flat registry of telemetry shapes; refrigeration is family #1, the rest discovered (DM-36)
PRO-4DeviceSchemaAssignment — resolves deviceId → (family, version) with confidence; the device_catalog resolver (DM-37)
PRO-5Unknown family quarantines, low confidence routes to review — never a silent null-map or drop (DM-37, DM-38)
PRO-6 ⚠Per-family typed hypertable + JSONB ext; the time-series store stays outside Postgres — only Alert rows cross in (shares the PTL-1 gap) (DM-38)
Federation (SPEC-PRD-FED) — consolidates the access model; every row maps through existing entities, no new model
FED-1UserLogin «AspNetUserLogins» + Organization.Sso — same landing as ID-3 (DM-1, DM-32)
FED-2ApplicationUser UNIQUE (Email, TenantId) + account chooser (DM-1)
FED-3 ⚠Federated session — runtime state, deliberately not persisted (same gap as ID-4)
FED-4ApplicationUser.EmailConfirmed locks org surfaces (DM-7)
FED-5Application.Gating × Tenant.Type + ApplicationUserRole decides the entitled set (DM-4)
FED-6ApplicationUserRole — UNIQUE (UserId, AppKey), no union mechanic (DM-4)
FED-7ApplicationRole rows per tenant + RolePermission matrix; custom roles are ordinary rows (DM-5, DM-6)
FED-8ApplicationRole.IsFixed — Owner / riversync admin always full (DM-6)
FED-9Permission catalog keyed per Application; matrix = RolePermission → AspNetRoleClaims (DM-6, DM-33)
FED-10RolePermission Scope overrides — region/site, narrow-only (DM-6)
FED-11AccessGrant — four scope switches + RegionLimit, bounded by covered devices (DM-16)
FED-12AuditEvent.AttributedOrganization on every cross-tenant action (DM-20)
FED-13ViewAsSession + AuditEvent.ViaSession — dual-logged, permission-gated (DM-20)
FED-14Implicit factory access — RiverSync is never a servicing PartnerId (DM-15)
FED-15Visibility rules over DM-4 (gating), DM-13 (no self-links), DM-25 (masked funnel) — UI mandates, no model impact
FED-16Canonical demo persona set — demo data convention, no model impact
Account (SPEC-APP-ACC)
ACC-1OrganizationDepartment tree · Region → OrganizationSite · type catalogs · BillingProfile · Invoice · AuditEvent (DM-8…11, DM-21)
ACC-2Role set (Owner fixed) + RolePermission matrix with Scope overrides (DM-4, DM-6)
ACC-3Partner list reads PartnerCustomer + PartnerProfile; detail reads the agreement cluster (DM-12…16)
ACC-4riversync-tenant view — standard identity cluster, no riversync-specific tables; landing fallback is a navigation rule, no model impact (DM-5)
ACC-5UserLogin + chooser (DM-1); ApplicationUser.EmailConfirmed locks org surfaces (DM-7)
ACC-6 ⚠ApplicationUser + UserLogin/UserToken cover profile & security; notification preferences not yet modeled
ACC-7UI rule (DS components only) — no model impact
Portal (SPEC-APP-PTL)
PTL-1 ⚠Device + Alert stream; telemetry time-series live outside the relational model (time-series store)
PTL-2Alert.State (open · acknowledged); Alert 0..1 ↔ 0..1 ServiceTicket (service)
PTL-3Per-device MaintenanceAgreement visibility — tier, PartnerId, PeriodEnd, Status=due ahead of renewal (DM-14)
PTL-4ServiceTicket · Visit · ChatThread with Counterpart = RiverSync or partner (service)
PTL-5Portal column of RolePermission gates acknowledge / configure / control (DM-4)
Partners (SPEC-APP-PAR app)
PAR-1Agreement-scoped reads — AccessGrant bounds every partner query (DM-16)
PAR-2Queue ordered by ServiceTicket.Sla ← device's MaintenanceTier at creation (DM-19)
PAR-3MaintenanceAgreement.Status = renewal due + SuccessorOrganization surface win/keep/lose (DM-14)
PAR-4PartnerProfile.ProgramTier + next-tier requirements (DM-17)
PAR-5Membership gating (DM-4); every action → AuditEvent.AttributedOrganization (DM-20)
PAR-6Deal registration writes Deal with PartnerAttrib = reseller + its channel (DM-24)
PAR-7Distributor funnel = masked Deal reads across handled resellers (DM-25)
PAR-8DistributionAgreement surface — each partner reads its own agreements (DM-23)
PAR-9Tier-gated functionality — Partners app resolves PartnerProfile.ProgramTier to its entitlements (DM-40)
PAR-10Reseller referral lead — Lead with PartnerAttrib = reseller, Source = partner, lands in the inbox (DM-46)
PAR-11Shared leads/opportunities — AssignedPartner set by RiverSync, tier-gated lead sharing (DM-47)
Sales — contacts, leads & opportunities (SPEC-PRD §7)
SAL-1Contact graph — Kind organization·person; ParentId / EmployerId / ManagerId hierarchies + ContactRelationship edges (DM-43)
SAL-2Contact graph distinct from tenancy; bridges org Contact → Tenant (CustomerId), person Contact → ApplicationUser (DM-44)
SAL-3Lead raised off a Contact — the single inbound front door (DM-45, DM-46)
SAL-4Lead.Source — six sources (online-form·telephone·showroom·trade-show·social·partner); optional Campaign (DM-46)
SAL-5Lead converts to an Opportunity, resolving the customer — one-way (DM-48)
SAL-6Opportunity + OpportunityVariant; confirmed variant → Deal.SourceOpportunity, seeds quote lines (DM-49, DM-50)
SAL-7Lead / Opportunity.AssignedPartner — tier-gated lead sharing (DM-47)
SAL-8Activity log anchored to a Contact + blended journey timeline (DM-51, DM-52)
SAL-9Unified communications inbox — Conversation (channel thread anchored to a Contact, foldered) + Message; each message also writes an Activity (DM-53, DM-54)
Pipeline (SPEC-APP-PIP)
PIP-1Quote + QuoteLine.ModelKey · MaintenanceTierKey — model FK → ProductModel + per-device maintenance tier on the line (commerce, DM-35)
PIP-2Won Deal → provisioning handoff → Device + first MaintenanceAgreement (DM-18)
PIP-3Deal.PartnerAttrib — a win creates / extends the PartnerCustomer (DM-18, DM-12)
PIP-4Channel chain on Deal — DistributorOrganization · DirectException, RiverSync allocation & overrule (DM-24)
PIP-5DirectException is per deal, RiverSync-granted case by case — no standing flag (DM-24)
PIP-6Reseller-registered deals enter Deal.Stage = intake for RiverSync qualification (DM-24)
PIP-7Reseller tier review & grant — PartnerTierReview written from the scorecard; grant sets ProgramTier (DM-41)
PIP-8Reseller→distributor conversion — Subtype change clears tier, registers DistributionAgreements (DM-42)
PIP-9Contacts directory + relationship graph view — Contact (Kind, hierarchies) + ContactRelationship (DM-43)
PIP-10Lead inbox across six sources; assign / share to partner (DM-46, DM-47)
PIP-11Qualify & convert — Lead → Opportunity, resolves customer (org Contact → Tenant) (DM-48)
PIP-12Opportunity + OpportunityVariant — variants quantified & compared (DM-49)
PIP-13Confirm variant → Deal.SourceOpportunity, seeds QuoteLines (DM-50)
PIP-14Activity timeline — Activity log blended with journey milestones (DM-51, DM-52)
PIP-15Communications inbox — Conversation + Message across seven channels, six folders, tied to contact & funnel records (DM-53, DM-54)
Admin (SPEC-APP-ADM)
ADM-1Tenant directory — Type, Plan, Status (suspend / restore is admin-role-gated) (provisioning)
ADM-2Provisioning — Device.Commissioned + SiteId; MaintenanceAgreement.SourceDeal back-reference (DM-18)
ADM-3Plan pricing across tenants; BillingProfile per org (DM-21)
ADM-4 ⚠Admin audit trail = AuditEvent; service status / uptime metrics live outside the relational model
ADM-5ViewAsSession + AuditEvent.ViaSession — one action, both logs (DM-20)
Field (SPEC-APP-FLD) — on-site execution; Field owns the Visit cluster (DM-27…31)
FLD-1Visit.Kind · Status (scheduled·enroute·onsite·complete); one source — ServiceTicket XOR MaintenancePlan (DM-27)
FLD-2Visit board per EngineerUser; OrganizationSite Contact/ArrivalInfo/DressCode read for the assignment (DM-31, DM-11)
FLD-3Visit.CheckInAt + serial confirm against Device.Serial (DM-27)
FLD-4VisitTask (check·measure·photo·action, Result, Reading); MaintenancePlan.NextDueAt advances on completion (DM-29)
FLD-5Corrective Visit.TicketId → ServiceTicket; remote commands stay in Devices (PTL-5)
FLD-6PartUsed (Sku, Name, Qty, Serial) per visit (DM-28)
FLD-7VisitEvidence (photo·doc, Uri, TaskId?) per visit/task (DM-28)
FLD-8VisitSignoff 1:1 with the visit — seals tasks/parts/evidence, unlocks ticket resolution (DM-30)
FLD-9Client-set Ids + CapturedAt on VisitTask/PartUsed/VisitEvidence/VisitSignoff — idempotent sync (DM-28)
FLD-10Membership gating — engineer-role on the riversync tenant (DM-4, AUTH-2/5); no new model
RiverSync Co., Ltd. · BangkokSPEC-ERD · 3 of 5

6Open modeling questions

Decisions the schema needs before it can freeze — mostly inherited from the PRDs' open questions, with two new ones the modeling exposed.

7Revision history

VersionDateChanges
0.112 Jun 2026First extraction — five domains, 27 entities, DM-1…21 integrity conditions, touchpoint matrix, open modeling questions
0.212 Jun 2026Postgres named as the target database; naming convention set — PascalCase entities, camelCase attributes, snake_case in DDL; diagram label and annotation legibility fixes
0.312 Jun 2026Attributes PascalCase too — every word capitalized (CustomerId, PeriodEnd), aligned with the .NET house style
0.412 Jun 2026Requirement traceability matrix — every TEN/ID/AUTH/PRT · ACC/PTL/PAR/PIP/ADM requirement mapped to entities and DM-rules; four acknowledged gaps flagged
0.512 Jun 2026Split into erd/ — this master + five per-app drill-downs; entities, DM-rules and diagram layouts centralized in erd-entities.js / erd-diagrams.js (single source of truth); three new drill-down diagrams (monitoring, service, audit, billing, provisioning)
0.612 Jun 2026Partner channel model (master PRT-9…12) — PartnerProfile.Subtype, DistributionAgreement entity, Deal channel fields (DistributorOrganization · DirectException), DM-22…25, channel diagram mounted in the Partners and Pipeline drill-downs; matrix, dictionary and traceability extended
0.713 Jun 2026Vocabulary — the "Fleet & partner coverage" domain renamed "Devices & partner coverage" (DM-12…17, domain map, coverage and monitoring diagram titles); no entity, relationship or rule changes (SPEC-PRD v0.12)
0.813 Jun 2026StaffRoleGrant dropped — riversync-tenant roles use the standard Role / AppRoleGrant model (SPEC-PRD v0.13): identity diagram, DM-5, traceability rows AUTH-5…7 · ACC-4 and the dictionary updated
0.913 Jun 2026SPEC-PRD-FED (Federation) traceability block — FED-1…16 mapped to the existing identity, authorization, coverage and audit entities; FED-3 flagged ⚠ (runtime session, the ID-4 gap); no entity or DM-rule changes
0.1013 Jun 2026Vocabulary — "staff" → user (SPEC-PRD v0.16): ViewAsSession.StaffUser → RiverSyncUser, Deal.OwnerStaff → OwnerUser, Application.Gating enum open · partner · riversync (was staff); DM-5/20/22/24 and the role-scope tokens reworded; no relationship or cardinality changes
0.1113 Jun 2026DM-26 added (SPEC-PRD PRT-13) — partner-role confidentiality: customer-facing partner surfaces read only PartnerCustomer + AccessGrant, never PartnerId's other tenant roles; PRT-13 traceability row added
0.1214 Jun 2026Field drill-down added (SPEC-PRD v0.18, SPEC-APP-FLD) — Visit extended (Kind, TicketId/PlanId source, DeviceId, Status, CheckInAt, CompletedAt) and moved Support → Field ownership; VisitTask · PartUsed · VisitEvidence · VisitSignoff · MaintenancePlan added; DM-27…31 (one-source visit, offline-first client-keyed captures, PM cadence, sign-off seals, scoped site info); fieldservice diagram; FLD-1…10 traceability; touchpoint matrix gains a Field column + footnote ⁷
0.1314 Jun 2026Identity re-grounded on ASP.NET Core Identity 10 (Guid keys) — the seven AspNet* tables become the base, extended not replaced: UserAccount→ApplicationUser «AspNetUsers» (IdentityUser<Guid>), IdpIdentity→UserLogin «AspNetUserLogins», Role→ApplicationRole «AspNetRoles», AppRoleGrant→ApplicationUserRole «AspNetUserRoles» (AppKey extension); UserClaim · UserToken · RoleClaim base tables added; EmailVerified→native EmailConfirmed. DM-32 (Identity foundation) and DM-33 (RolePermission matrix compiles to AspNetRoleClaims/AspNetUserClaims) added; DM-1/4/5/6/7 reworded; identity diagram split into tenancy/identity + authorization; dictionary, traceability (ID/AUTH/FED/ACC rows) updated. No requirement, cardinality or relationship changes
0.1414 Jun 2026Maintenance level catalog introduced — new Maintenance entity (the broadly-available maintenance levels: name, SLA, response class, general description) and the MaintenanceAgreement → DeviceMaintenance rename. The per-device Tier enum and QuoteLine.MaTier become a MaintenanceKey FK → Maintenance, so device, quote line and ticket SLA resolve to one shared level. DM-34 added; DM-17/18/19 reworded; coverage diagram gains the catalog box; matrix row, traceability (PRT-1/2/5/7, PTL-3, PAR-2/3, PIP-1/2, ADM-2) and the entity dictionary updated. No requirement, cardinality or relationship changes (realizes PRT-5, PIP-1)
0.1515 Jun 2026Coverage → Agreement domain rename (platform-wide). Entity Maintenance → MaintenanceAgreement, DeviceMaintenance → DeviceAgreement, FK MaintenanceKey → MaintenanceAgreementKey; the domain context is now Agreement (coverage = the scope an agreement covers). Matrix label, traceability (PRT-1/2/5/7, ACC-3, PTL-3, PAR-1/2/3, PIP-1/2, ADM-2), open questions and the dictionary updated; no cardinality or relationship changes.
0.1615 Jun 2026Entity SiteLocation → OrganizationSite (spelled-out naming, Jun 2026) — the customer's site facility. Renamed in erd-entities.js and every diagram/route reference (group ids, the Region → OrganizationSite relation, the Devices/monitoring/provisioning diagrams); matrix, dictionary and traceability (ACC-1, FLD-2) re-render. Mirrors the Account app menu Site Locations → Sites (SPEC-APP-ACC v0.5). No cardinality or relationship changes.
0.1716 Jun 2026Entity OrganizationUnit → OrganizationDepartment (spelled-out, domain-context naming, Jun 2026) — the customer's reporting-structure node. Renamed in erd-entities.js (entity + the ApplicationUser.OrganizationDepartmentId FK), every diagram reference (the structure diagram + domain map card + parent self-relation + notes) and the domain catalog/routes; matrix, dictionary, open question and traceability (ACC-1, DM-8…9) re-render. Mirrors the Account app surface Departments (SPEC-APP-ACC); EntityType still labels each node (company · division · department · team). No cardinality or relationship changes.
0.1826 Jun 2026Device model enum + Agreement → Maintenance ownership move. Device.Model now reads frigo · koelkast · nevera (Nevera family added, Portal SPEC-APP-PTL v0.3). The renamed Maintenance service (was Agreement; SVC-MNT) owns MaintenanceAgreement · DeviceAgreement · PartnerCustomer · AccessGrant; PartnerProfile and DistributionAgreement moved to Sales (SVC-13). The DM-rule group "Devices & agreements" → "Devices & maintenance" in erd-entities.js; entity names and cardinalities unchanged. Mirrors SPEC-DDD v0.10 and the new Maintenance PRD (SPEC-PRD-MNT).
0.1926 Jun 2026Entity rename — the per-device record is the maintenance agreement. MaintenanceAgreement → MaintenanceTier (the Silver · Gold catalog, aligning with the DS TierBadge) and DeviceAgreement → MaintenanceAgreement (the per-device record). FK MaintenanceAgreementKey → MaintenanceTierKey on MaintenanceAgreement and QuoteLine; ServiceTicket.Sla now reads ← MaintenanceTier. Catalog (erd-entities.js), all diagrams (erd-diagrams.js), domain & workflow catalogs, correlation spine, matrix, traceability and dictionary re-render. No cardinality or relationship changes.
0.2027 Jun 2026Customer / partner tenant FK naming convention. CustomerOrganization → CustomerId and PartnerOrganization → PartnerId on every entity (Device, MaintenanceAgreement, Deal, ServiceTicket, ChatThread) and across all DM-rules, routes and prose. The derived link entity PartnerLink → PartnerCustomer with explicit CustomerId · PartnerId PF keys (CHECK CustomerId ≠ PartnerId, DM-13). Applied platform-wide through the PRD, ERD, Domain and Workflow sets — catalog-driven docs (erd-entities.js, domain-catalog.js / -routes.js, workflow-catalog.js) re-render. No cardinality or relationship changes.
0.2127 Jun 2026The device model becomes an entity — products & telemetry taxonomy. The free-text Device.Model enum (frigo · koelkast · nevera) is replaced by four catalog entities: ProductFamily → ProductModel (the commercial line; ProductModel is the single Product entity carrying both commercial spec and its declared telemetry schema), TelemetrySchemaFamily (the flat registry of telemetry shapes — refrigeration is family #1, the rest discovered from the live fleet) and DeviceSchemaAssignment (resolves deviceId → family + version). Device.Model → Device.ModelKey and QuoteLine.Model → QuoteLine.ModelKey FKs → ProductModel. New rule group DM-35–38; PRO-1…6 traceability block (PRO-6 ⚠ shares the PTL-1 time-series gap); taxonomy diagram added to the domain map and the new Product Catalog drill-down (SPEC-ERD-PRO); domain map card and dictionary updated. Realizes Initiative 015 + epics 078–080 (Telemetry/Discovery). Catalog-driven — erd-entities.js / erd-diagrams.js re-render.
0.2227 Jun 2026Authoritative product classification. ProductFamily gains its class (frigo & koelkast = edge micro data center, nevera = modular micro data center); ProductModel keys set to frigo-20 · frigo-30 · koelkast-20 · koelkast-30 · nevera-30 · nevera-40 (replaces illustrative numbers). DM-35 and the taxonomy diagram hints re-render from the catalog; Product Catalog drill-down v0.2. Mirrors SPEC-PRD v0.24. ⚠ frigo's second model read as frigo-30 (source duplicated “frigo-20”) — pending confirmation. No cardinality or relationship changes.
0.2328 Jun 2026Reseller program tier model (SPEC-PRD PRT-14…17). PartnerProfile gains TierGrantedAt · TierReviewState and its ProgramTier is scoped reseller-only (NULL for distributors); new PartnerTierReview entity (scorecard inputs + grant·hold·downgrade decision). New rules DM-39…42 (tier scope · tier→entitlement resolution · review-driven grant · reseller→distributor conversion); DM-17 amended. Channel diagram gains PartnerTierReview; §2 matrix adds a Partner program tier & review row (footnote ⁹); traceability adds PRT-14…17, PAR-9, PIP-7…8. Cascaded from SPEC-PRD v0.32 / SPEC-APP-PAR v0.5 / SPEC-APP-PIP v0.4; SPEC-DDD (Sales SVC-17, partner.tier-changed / partner.subtype-changed), SPEC-DWF (PartnerProfile lifecycle) and SPEC-PWF (Tier progression · Reseller→distributor) follow. Distributor program-tier tags retired in the reference orgs. Catalog-driven — erd-entities.js / erd-diagrams.js re-render.
0.2428 Jun 2026Sales CRM front of the funnel (SPEC-PRD §7, SAL-1…8). New entities Contact (party — organization·person, with ParentId/EmployerId/ManagerId hierarchies), ContactRelationship, Campaign, Lead, Opportunity, OpportunityVariant and Activity; Deal gains SourceOpportunity. New rules DM-43…52 (contact graph & tenancy bridge, lead front door & sources, lead sharing, lead→opportunity conversion, opportunity variants, deal confirmation, activity log & blended timeline). §5 traceability gains the SAL-1…8 block plus PIP-9…14 and PAR-10/11; entity dictionary updated. Cascaded from SPEC-PRD v0.33 / SPEC-DDD (Sales owns the CRM, SVC-18) / SPEC-APP-PIP v0.5 / SPEC-APP-PAR v0.6. Pipeline ERD gains the sales-CRM diagram. SPEC-DWF / SPEC-PWF lifecycles to follow.
0.2528 Jun 2026Unified communications inbox (SPEC-PRD SAL-9). New Sales entities Conversation (one channel thread — web-form·email·line·instagram·facebook·linkedin·phone — anchored to a Contact, foldered inbox·draft·sent·archived·junk·deleted, optionally relating to a funnel record) and Message (direction, body, delivery state, attachments). New rules DM-53 (Conversation is the unified comms thread, distinct from ChatThread & Activity, each message also writes an Activity so the blended timeline holds) and DM-54 (folders are soft, reversible states; deleted retained per LIF-1; channel is the reply path). §5 traceability gains SAL-9 and PIP-15; entity dictionary adds Conversation · Message. Cascaded from SPEC-PRD v0.34 / SPEC-APP-PIP v0.8 / SPEC-PRD-SAL v0.2; SPEC-DDD Sales (Conversation/Message ownership, message.* / conversation.* events). Catalog-driven — erd-entities.js re-renders; Pipeline ERD diagram inclusion to follow.
0.2628 Jun 2026Inbox forwarding (DM-54 amended). DM-54 now records that a reply or forward goes out on the conversation's own channel — Forward carries the conversation, Forward all the entire thread (every Message) — each forward composing a new outbound Message and emitting conversation.forwarded. Cascaded from SPEC-PRD SAL-9 (amended) / SPEC-APP-PIP PIP-15; SPEC-DDD Sales gains the conversation.forwarded event + forward route. Catalog-driven — erd-entities.js re-renders.
RiverSync Co., Ltd. · BangkokSPEC-ERD · 4 of 5

8Entity dictionary

Identity is grounded in ASP.NET Core Identity 10 (Guid keys): the seven AspNet* tables are the base; the Application* entities subclass the Identity types and add the RiverSync extension columns. Base tables carry their «AspNet…» table name. (DM-32, DM-33)

EntityKeysPurpose / notable attributes
TenantIdType (riversync · customer · partner) + role flags, plan, status — RiverSync extension
OrganizationId · UQ TenantId0..1 per tenant; legal name, domain, currency, Entra SSO, branding — extension
ApplicationUser «AspNetUsers»Id (Guid) · UQ (Email, Tenant)IdentityUser<Guid> + ext: TenantId, DisplayName, Status, EmailConfirmed (native), LastActive, OrganizationDepartment, primary site
UserLogin «AspNetUserLogins»PK (Provider, Key)Google · Microsoft · LinkedIn · Entra external logins; email & password is the AspNetUsers PasswordHash
UserClaim · UserToken · RoleClaimId · PF · IdIdentity base: tenant/scope claims on the token; verify/2FA/refresh tokens; compiled permission claims (DM-33)
ApplicationKeythe six apps; gating (open · partner · riversync) — RiverSync extension
ApplicationRole «AspNetRoles»Id (Guid)IdentityRole<Guid> + ext: TenantId, Kind, IsFixed for Owner / riversync admin
ApplicationUserRole «AspNetUserRoles»PF (User, Role) · UQ (User, App)IdentityUserRole<Guid> + AppKey extension — one role per app per user
Permission · RolePermissionKey · PF (Role, Perm)extension: the per-app authored matrix with region/site scope, compiled to AspNetRoleClaims (DM-33)
OrganizationDepartment · EntityTypeId · Keycompany → division → team tree; editable type catalog
Region · OrganizationSite · SiteTypeId · Id · Keyregion-grouped facilities; status, contact, support-visit info
SiteAssignmentPF (User, Site)people working at a site
ProductFamilyKeycommercial product line — frigo · koelkast · nevera; class, cooling range, schema generation (DM-35)
ProductModelKey · FK ProductFamilyKeythe one Product entity: model within a family + its DECLARED telemetry schema (SchemaFamilyKey + version); Device.ModelKey / QuoteLine.ModelKey resolve here (DM-35, DM-36)
TelemetrySchemaFamilyKeyflat registry of telemetry shapes — refrigeration is family #1 (60-col), the rest discovered; id-pattern, typed fields, twin shape, storage target (DM-36)
DeviceSchemaAssignmentDeviceId · FK SchemaFamilyKeyresolves a device's IoT-Hub id to its actual (family, version) with confidence; resolved · review · quarantine (DM-37, DM-38)
DeviceId (MDC-####) · FK ModelKeymodel → ProductModel (was a Model enum, v0.21), site, customer org, commissioned
MaintenanceTierKey (silver · gold)catalog of maintenance tiers — name, SLA (8×5 NBD · 24×7 ND), response class, general description; broadly available (DM-34)
MaintenanceAgreementId (MA-YYYY-NNNN)per device; MaintenanceTierKey → MaintenanceTier, period, status, successor partner, source deal
PartnerCustomerPF (CustomerId, PartnerId)derived from agreements; never a self-link
AccessGrantUQ LinkId4 scope switches + optional region limit, effective immediately
PartnerProfileOrganizationIdprogram tier: Authorized · Gold · Platinum; subtype: distributor · reseller (PRT-9)
DistributionAgreementId (DA-…)reseller sources through distributor; scope (region · product line), status, since (PRT-10)
ContactId (CON-…) · KindCRM party — organization·person; ParentId / EmployerId / ManagerId hierarchies; CustomerId / ApplicationUserId bridge to tenancy (DM-43, DM-44)
ContactRelationship · CampaignId · Idwider graph edges (influences·introduced-by·advises); marketing campaign for source attribution
LeadId (LEAD-…)inbound front door off a Contact; Source (six), PartnerAttrib / AssignedPartner; converts to an Opportunity (DM-45…48)
Opportunity · OpportunityVariantId (OPP-…) · PF (Opp, No)qualified pursuit with comparable variants; confirmed variant → Deal.SourceOpportunity (DM-49, DM-50)
ActivityId (ACT-…)touchpoint log against a Contact (email·call·visit·survey…), blended journey timeline (DM-51, DM-52)
Conversation · MessageId (CNV-…) · PF (Cnv, Seq)unified-inbox thread on one channel (web-form·email·line·instagram·facebook·linkedin·phone) anchored to a Contact, foldered (inbox·draft·sent·archived·junk·deleted); each Message writes an Activity (DM-53, DM-54)
Deal · Quote · QuoteLineId · Id · PF (Quote, Line)stage, value, reseller attribution + channel (distributor or direct exception); lines carry ModelKey → ProductModel + maintenance-tier level
Plan · BillingProfile · InvoiceId · OrganizationId · Idplatform plans; THB profile; invoice status (paid · due · overdue)
Alert · ServiceTicketIdalarm → ticket; SLA denormalized from the device's maintenance-agreement level at creation
Visit · VisitTask · PartUsedId · per visit · per visiton-site visit (corrective/preventive, status, check-in); runbook tasks with readings; parts fitted — owned by Field
VisitEvidence · VisitSignoff · MaintenancePlanId · VisitId · Idphotos/docs; the sealing customer sign-off (1:1); per-device PM cadence + next-due
ChatThreadIdcustomer ↔ riversync or partner; carries alert/appointment cards
AuditEvent · ViewAsSessionId · Idper-tenant log; actor, attributed org, view-as session, target, IP
RiverSync Co., Ltd. · BangkokSPEC-ERD · 5 of 5